/*******************************************************************************
**3456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789
**      10        20        30        40        50        60        70        80
**
** file:
**    events.c
**
** author:
**    Mirco "MacSlow" Mueller <macslow@bangang.de>
**
** copyright (C) Mirco Mueller, 2006/2007, placed under the terms of the GPL
**
*******************************************************************************/

#include "events.h"
#include "cairo-rendering.h"

#include <gdk/gdkkeysyms.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

#include "globals.h"

GLfloat g_afAngle[3] = {260.0f, -58.0f, 0.0f};
GLfloat g_fScale = 1.2f;
GLfloat g_fAlpha = 1.0f;
gdouble g_fCurrentX;
gdouble g_fCurrentY;
gdouble g_fDeltaX;
gdouble g_fDeltaY;
gdouble g_fLastX;
gdouble g_fLastY;
gboolean g_bLMBPressed = FALSE;
gboolean g_bRMBPressed = FALSE;
GLuint g_auiColorBuffer[3];

gboolean
delete_handler(GtkWidget *pWidget,
			   GdkEvent *pEvent,
			   gpointer data)
{
	glDeleteTextures(3, g_auiColorBuffer);
	gtk_main_quit();
	return TRUE;
}

gboolean
button_handler(GtkWidget *pWidget,
			   GdkEventButton *pEvent,
			   gpointer data)
{
	switch (pEvent->button)
	{
	case 1:
		if (pEvent->type == GDK_BUTTON_PRESS)
		{
			g_fCurrentX = pEvent->x;
			g_fCurrentY = pEvent->y;
			g_fLastX = g_fCurrentX;
			g_fLastY = g_fCurrentY;
			g_bLMBPressed = TRUE;
		}
		else if (pEvent->type == GDK_BUTTON_RELEASE)
			g_bLMBPressed = FALSE;
		break;

	case 3:
		if (pEvent->type == GDK_BUTTON_PRESS)
		{
			g_fCurrentX = pEvent->x;
			g_fCurrentY = pEvent->y;
			g_fLastX = g_fCurrentX;
			g_fLastY = g_fCurrentY;
			g_bRMBPressed = TRUE;
		}
		else if (pEvent->type == GDK_BUTTON_RELEASE)
			g_bRMBPressed = FALSE;
		break;
	}

	return TRUE;
}

gboolean
scroll_handler(GtkWidget *pWidget,
			   GdkEventScroll *pEvent,
			   gpointer data)
{
	switch (pEvent->direction)
	{
	case GDK_SCROLL_UP:
		if (g_fAlpha < 1.0f)
			g_fAlpha += 0.05f;
		break;

	case GDK_SCROLL_DOWN:
		if (g_fAlpha > 0.0f)
			g_fAlpha -= 0.05f;
		break;

	/* just silence gcc */
	default:
		break;
	}

	return TRUE;
}

gboolean
motion_notify_handler(GtkWidget *pWidget,
					  GdkEventMotion *pEvent,
					  gpointer data)
{
	if (g_bLMBPressed)
	{
		g_fCurrentX = pEvent->x;
		g_fCurrentY = pEvent->y;
		g_fDeltaX = g_fLastX - g_fCurrentX;
		g_fDeltaY = g_fLastY - g_fCurrentY;
		g_fLastX = g_fCurrentX;
		g_fLastY = g_fCurrentY;

		g_afAngle[0] -= g_fDeltaX;
		g_afAngle[1] -= g_fDeltaY;
		gtk_widget_queue_draw(pWidget);
	}

	if (g_bRMBPressed)
	{
		g_fCurrentY = pEvent->y;
		g_fDeltaY = g_fLastY - g_fCurrentY;
		g_fLastY = g_fCurrentY;

		g_fScale -= g_fDeltaY / 100.0f;
		gtk_widget_queue_draw(pWidget);
	}

	return TRUE;
}

void screen_changed_handler(GtkWidget *pWidget,
							GdkScreen *pOldScreen,
							gpointer data)
{
	GdkScreen *pScreen = gtk_widget_get_screen(pWidget);
	GdkColormap *pColormap = gdk_screen_get_rgba_colormap(pScreen);

	if (!pColormap)
	{
		g_print("Could not get a RGBA-colormap. Using plain RGB.\n");
		pColormap = gdk_screen_get_rgb_colormap(pScreen);
	}

	gtk_widget_set_colormap(pWidget, pColormap);
}

void realize_handler(GtkWidget *pWidget,
					 gpointer data)
{
	GLfloat afLightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
	GdkGLContext *pGlContext = gtk_widget_get_gl_context(pWidget);
	GdkGLDrawable *pGlDrawable = gtk_widget_get_gl_drawable(pWidget);

	/* make GL-context "current" */
	if (!gdk_gl_drawable_gl_begin(pGlDrawable, pGlContext))
		return;

	glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	glClearDepth(1.0f);
	glDisable(GL_DEPTH_TEST);
	glEnable(GL_NORMALIZE);
	glEnable(GL_BLEND);
	glBlendFunc(GL_ONE, GL_ONE);
	glShadeModel(GL_FLAT);
	glEnable(GL_TEXTURE_RECTANGLE_ARB);
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	glDisable(GL_CULL_FACE);
	glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, 0.0f);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, afLightDiffuse);

	g_print("OpenGL version: %s\n", glGetString(GL_VERSION));
	g_print("OpenGL vendor: %s\n", glGetString(GL_VENDOR));
	g_print("OpenGL renderer: %s\n", glGetString(GL_RENDERER));

	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
					GL_TEXTURE_MIN_FILTER,
					GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,
					GL_TEXTURE_MAG_FILTER,
					GL_LINEAR);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0.0f, 0.0f, Z_NEAR,
			  0.0f, 0.0f, 0.0f,
			  0.0f, 1.0f, 0.0f);
	glTranslatef(0.0f, 0.0f, -Z_NEAR);

	/* end drawing to current GL-context */
	gdk_gl_drawable_gl_end(pGlDrawable);
}

gboolean
configure_handler(GtkWidget *pWidget,
				  GdkEventConfigure *pEvent,
				  gpointer data)
{
	GdkGLContext *pGlContext = gtk_widget_get_gl_context(pWidget);
	GdkGLDrawable *pGlDrawable = gtk_widget_get_gl_drawable(pWidget);
	GLsizei w = pWidget->allocation.width;
	GLsizei h = pWidget->allocation.height;

	/* make GL-context "current" */
	if (!gdk_gl_drawable_gl_begin(pGlDrawable, pGlContext))
		return FALSE;

	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(2.0f * FOVY_2, (GLfloat)w / (GLfloat)h, 0.1f, 50.0f);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glGenTextures(3, g_auiColorBuffer);
	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, g_auiColorBuffer[0]);
	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
				 0,
				 GL_RGBA,
				 g_iWidth,
				 g_iHeight,
				 0,
				 GL_BGRA,
				 GL_UNSIGNED_BYTE,
				 NULL);

	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, g_auiColorBuffer[1]);
	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
				 0,
				 GL_RGBA,
				 g_iWidth,
				 g_iHeight,
				 0,
				 GL_BGRA,
				 GL_UNSIGNED_BYTE,
				 NULL);

	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, g_auiColorBuffer[2]);
	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
				 0,
				 GL_RGBA,
				 g_iWidth,
				 g_iHeight,
				 0,
				 GL_BGRA,
				 GL_UNSIGNED_BYTE,
				 NULL);

	/* end drawing to current GL-context */
	gdk_gl_drawable_gl_end(pGlDrawable);

	return TRUE;
}

gboolean
expose_handler(GtkWidget *pWidget,
			   GdkEventExpose *pEvent,
			   gpointer data)
{
	GdkGLContext *pGlContext = gtk_widget_get_gl_context(pWidget);
	GdkGLDrawable *pGlDrawable = gtk_widget_get_gl_drawable(pWidget);
	static gint iFrames = 0;
	static gdouble fLastTimeStamp = 0.0f;
	;
	static gdouble fCurrentTimeStamp = 0.0f;
	;
	static gdouble fLastFullSecond = 0.0f;
	;
	gdouble fFrameTimeDelta = 0.0f;
	gdouble fFullSecond = 0.0f;
	GLfloat afVector[3];
	GLfloat afMatrixBase[16];
	GLfloat afFrontDiffuseMat[] = {1.0f * g_fAlpha,
								   1.0f * g_fAlpha,
								   1.0f * g_fAlpha,
								   1.0f * g_fAlpha};
	GLfloat afBackDiffuseMat[] = {1.0f * g_fAlpha,
								  1.0f * g_fAlpha,
								  1.0f * g_fAlpha,
								  1.0f * g_fAlpha};

	/* make GL-context "current" */
	if (!gdk_gl_drawable_gl_begin(pGlDrawable, pGlContext))
		return FALSE;

	glPushMatrix();
	glTranslatef(0.0f, 0.0f, 0.5f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glGetFloatv(GL_MODELVIEW_MATRIX, afMatrixBase);
	afVector[0] = afMatrixBase[0];
	afVector[1] = afMatrixBase[4];
	afVector[2] = afMatrixBase[8];
	glRotatef(g_afAngle[1] * 0.5f, afVector[0], afVector[1], afVector[2]);
	afVector[0] = afMatrixBase[1];
	afVector[1] = afMatrixBase[5];
	afVector[2] = afMatrixBase[9];
	glRotatef(g_afAngle[0] * 0.5f, afVector[0], afVector[1], afVector[2]);
	glScalef(g_fScale, g_fScale, g_fScale);

	glMaterialfv(GL_FRONT, GL_DIFFUSE, afFrontDiffuseMat);
	glMaterialfv(GL_BACK, GL_DIFFUSE, afBackDiffuseMat);

	render_zini(g_pCairoContext[0], g_iWidth, g_iHeight);
	advance(&g_lineOne.start);
	advance(&g_lineOne.end);
	advance(&g_lineTwo.start);
	advance(&g_lineTwo.end);
	render_curve(g_pCairoContext[1],
				 g_iWidth,
				 g_iHeight,
				 &g_lineOne,
				 &g_lineTwo);
	render_flower(g_pCairoContext[2], g_iWidth, g_iHeight);

	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, g_auiColorBuffer[0]);
	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
				 0,
				 GL_RGBA,
				 g_iWidth,
				 g_iHeight,
				 0,
				 GL_BGRA,
				 GL_UNSIGNED_BYTE,
				 g_pucSurfaceData[0]);

	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, g_auiColorBuffer[1]);
	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
				 0,
				 GL_RGBA,
				 g_iWidth,
				 g_iHeight,
				 0,
				 GL_BGRA,
				 GL_UNSIGNED_BYTE,
				 g_pucSurfaceData[1]);

	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, g_auiColorBuffer[2]);
	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB,
				 0,
				 GL_RGBA,
				 g_iWidth,
				 g_iHeight,
				 0,
				 GL_BGRA,
				 GL_UNSIGNED_BYTE,
				 g_pucSurfaceData[2]);

	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, g_auiColorBuffer[0]);
	glBegin(GL_QUADS);
	glNormal3f(1.0f, 0.0f, 0.0f);
	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(1.0f, 1.0f, 1.0f);
	glTexCoord2f((GLfloat)g_iWidth, 0.0f);
	glVertex3f(1.0f, 1.0f, -1.0f);
	glTexCoord2f((GLfloat)g_iWidth, (GLfloat)g_iHeight);
	glVertex3f(1.0f, -1.0f, -1.0f);
	glTexCoord2f(0.0f, (GLfloat)g_iHeight);
	glVertex3f(1.0f, -1.0f, 1.0f);

	glNormal3f(0.0f, 1.0f, 0.0f);
	glTexCoord2f(0.0f, (GLfloat)g_iHeight);
	glVertex3f(1.0f, 1.0f, 1.0f);
	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(1.0f, 1.0f, -1.0f);
	glTexCoord2f((GLfloat)g_iWidth, 0.0f);
	glVertex3f(-1.0f, 1.0f, -1.0f);
	glTexCoord2f((GLfloat)g_iWidth, (GLfloat)g_iHeight);
	glVertex3f(-1.0f, 1.0f, 1.0f);
	glEnd();

	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, g_auiColorBuffer[1]);
	glBegin(GL_QUADS);
	glNormal3f(0.0f, 0.0f, 1.0f);
	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(1.0f, 1.0f, 1.0f);
	glTexCoord2f((GLfloat)g_iWidth, 0.0f);
	glVertex3f(-1.0f, 1.0f, 1.0f);
	glTexCoord2f((GLfloat)g_iWidth, (GLfloat)g_iHeight);
	glVertex3f(-1.0f, -1.0f, 1.0f);
	glTexCoord2f(0.0f, (GLfloat)g_iHeight);
	glVertex3f(1.0f, -1.0f, 1.0f);

	glNormal3f(0.0f, -1.0f, 0.0f);
	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(1.0f, -1.0f, 1.0f);
	glTexCoord2f((GLfloat)g_iWidth, 0.0f);
	glVertex3f(1.0f, -1.0f, -1.0f);
	glTexCoord2f((GLfloat)g_iWidth, (GLfloat)g_iHeight);
	glVertex3f(-1.0f, -1.0f, -1.0f);
	glTexCoord2f(0.0f, (GLfloat)g_iHeight);
	glVertex3f(-1.0f, -1.0f, 1.0f);
	glEnd();

	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, g_auiColorBuffer[2]);
	glBegin(GL_QUADS);
	glNormal3f(-1.0f, 0.0f, 0.0f);
	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(-1.0f, 1.0f, 1.0f);
	glTexCoord2f((GLfloat)g_iWidth, 0.0f);
	glVertex3f(-1.0f, 1.0f, -1.0f);
	glTexCoord2f((GLfloat)g_iWidth, (GLfloat)g_iHeight);
	glVertex3f(-1.0f, -1.0f, -1.0f);
	glTexCoord2f(0.0f, (GLfloat)g_iHeight);
	glVertex3f(-1.0f, -1.0f, 1.0f);

	glNormal3f(0.0f, 0.0f, -1.0f);
	glTexCoord2f(0.0f, (GLfloat)g_iHeight);
	glVertex3f(1.0f, 1.0f, -1.0f);
	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(-1.0f, 1.0f, -1.0f);
	glTexCoord2f((GLfloat)g_iWidth, 0.0f);
	glVertex3f(-1.0f, -1.0f, -1.0f);
	glTexCoord2f((GLfloat)g_iWidth, (GLfloat)g_iHeight);
	glVertex3f(1.0f, -1.0f, -1.0f);
	glEnd();

	glPopMatrix();

	if (gdk_gl_drawable_is_double_buffered(pGlDrawable))
		gdk_gl_drawable_swap_buffers(pGlDrawable);
	else
		glFlush();

	/* end drawing to current GL-context */
	gdk_gl_drawable_gl_end(pGlDrawable);

	fCurrentTimeStamp = g_timer_elapsed(g_pTimerId, &g_ulMilliSeconds);
	g_ulMilliSeconds /= 1000;
	fFrameTimeDelta = fCurrentTimeStamp - fLastTimeStamp;
	fFullSecond = fCurrentTimeStamp - fLastFullSecond;

	if (fFullSecond < 1.0f)
		iFrames++;
	else
	{
		g_print("fps: %d, last frame-time: %f\n",
				iFrames,
				fFrameTimeDelta);
		iFrames = 0;
		fLastFullSecond = fCurrentTimeStamp;
	}

	fLastTimeStamp = fCurrentTimeStamp;

	return TRUE;
}

gboolean
key_press_handler(GtkWidget *pWidget,
				  GdkEventKey *pEvent,
				  GMainLoop *pLoop)
{
	if (pEvent->type == GDK_KEY_PRESS)
	{
		switch (pEvent->keyval)
		{
		case GDK_q:
		case GDK_Escape:
			delete_handler(pWidget, NULL, NULL);
			break;

		case GDK_Page_Up:
			if (g_fAlpha < 1.0f)
				g_fAlpha += 0.05;
			break;

		case GDK_Page_Down:
			if (g_fAlpha > 0.0f)
				g_fAlpha -= 0.05;
			break;

		default:
			break;
		}
	}

	return TRUE;
}

gboolean
draw_handler(GtkWidget *pWidget)
{
	gtk_widget_queue_draw(pWidget);

	return TRUE;
}
